package com.snap.vseries.service;

import android.os.Bundle;
import android.util.Log;
import android.util.SparseArray;

import com.orhanobut.logger.Logger;
import com.snap.vseries.AppConfig;
import com.snap.vseries.bean.ExpInfo;
import com.snap.vseries.bean.LightBean;
import com.snap.vseries.bean.ProcessBean;
import com.snap.vseries.bean.ProgramSection;
import com.snap.vseries.bean.ProgramStage;
import com.snap.vseries.bean.SettingInfo;
import com.snap.vseries.bean.TempBean;
import com.snap.vseries.logger.LoggerUtil;
import com.snap.vseries.manager.ConfigManager;
import com.snap.vseries.serial.Cmd;
import com.snap.vseries.serial.SerialPortHelper;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;

import static com.snap.vseries.AppConfig.TOTAL_CHANNEL_COUNT;
import static com.snap.vseries.Constants.EXP_FINISH_MANUALLY;


/**
 * 单个实验记录在 实验名$运行仓位.grp文件中
 * 单个实验详细log在 实验名$运行仓位.txt文件中
 * 单个实验的日志（UI展示的） 记录在List<ExpLog>中
 * <p>
 * 一份实验包含多个group
 */
public class ProgramTask implements Callable<Boolean>, IExpDataSource, IExpFinalOperation {

    private static final String TAG = "CTask";
    private final ExpInfo expInfo;
    private final long planTime;
    private final String name;
    /**
     * 实验：实验温度
     */
    volatile List<TempBean> tempList;
    private SparseArray<List<LightBean>> lightData;
    volatile List<ProcessBean> processData;
    private OnSectionProcessListener onSectionProcessListener;
    private SerialPortHelper serialPortHelper;
    private List<ProgramStage> pcrProgram;
    private long beginTime;
    private long pcrBeginTime;
    private int pcrState;
    private int lightIndex;
    private OnIntensityListener onIntensityListener;
    private long leftTime;
    private float currentTemp;
    private int totalCycle;
    /**
     * 表示当前正在执行的段序号，从1开始
     */
    private int currentStageIndex = -1;
    /**
     * 表示当前正在执行的结点序号，从1开始
     */
    private int currentSectionIndex = -1;
    /**
     * 当前段是否循环运行
     */
    private boolean isCurrentStageCycle = false;
    /**
     * 当前段循环到第几次了
     */
    private int runningCycleInStage = 0;
    /**
     * 当前运行过的所有段的循环次数
     */
    private int totalRunCycleCount = 0;
    private  int deviceId;
    private int timeOutCount;

    public ProgramTask(SerialPortHelper serialPortHelper, ExpInfo expInfo, int deviceId) {
        this.deviceId = deviceId;
        this.serialPortHelper = serialPortHelper;
        this.pcrProgram = expInfo.getProgram();
        beginTime = System.currentTimeMillis();
        this.name=expInfo.getName();
        //目前温度数据只有一个list，这里考虑到之后温度数据可能也会有多条list,所以又套了层集合，比如做了平滑之后的数据
        SparseArray<List<TempBean>> tempData = new SparseArray<>();
        tempData.put(0, tempList = new ArrayList<>());
        //温度数据
        expInfo.setTempData(tempData);
        lightData = new SparseArray<>();
        for (int i = 0; i < TOTAL_CHANNEL_COUNT; i++) {
            //最终展示给用户看的
            lightData.put(i, new ArrayList<>());
            //最原始的
            lightData.put(1000 + i, new ArrayList<>());
            //荧光校准后的
            lightData.put(2000 + i, new ArrayList<>());
        }
        expInfo.setLightData(lightData);
        processData = new ArrayList<>();
        planTime = (long) (getTotalTime() * 1000);
        LoggerUtil.Log(deviceId,name,"预计运行时间,planTime=" + planTime);
        this.expInfo = expInfo;
        expInfo.setDeviceTag(AppConfig.DEVICES[deviceId]);
        for (ProgramStage programStage : pcrProgram) {
            if (programStage.getCycleCount() > 1) {
                totalCycle += programStage.getCycleCount();
            }
        }
    }

    // TODO: 2020/3/5 运行时间
    private float getTotalTime() {
        SettingInfo settingInfo = ConfigManager.getInstance().getSettingInfo();
        float total = 1f;
        float lastTemp = 25f;
        for (int i = 0; i < pcrProgram.size(); i++) {
            ProgramStage stage = pcrProgram.get(i);
            for (int k = 0; k < stage.getCycleCount(); k++) {
                for (int j = 0; j < stage.getSections().size(); j++) {
                    ProgramSection section = stage.getSections().get(j);
                    total += section.getDuration();
                    float deltaTemp = section.getTemp() - lastTemp;
                    total += Math.abs(deltaTemp) / (deltaTemp > 0 ? settingInfo.getIncreaseVelocity() : settingInfo.getDecreaseVelocity());
                    lastTemp = section.getTemp();
                }
            }
        }
        return total;
    }

    @Override
    public Boolean call() throws Exception {
        Bundle bundle;
        LoggerUtil.Log(deviceId,name,"实验开始");
        //pcr扩增程序
        if (pcrProgram == null || pcrProgram.size() == 0) {
            processData.add(new ProcessBean(10, "无PCR扩增程序"));
        } else {
            LoggerUtil.Log(deviceId,name,"PCR扩增开始");
            processData.add(new ProcessBean(10, "PCR扩增开始"));

            bundle = serialPortHelper.execute(Cmd.pcrProgram(deviceId, pcrProgram));
            if (!bundle.getBoolean("suc")) {
                LoggerUtil.Log(deviceId,name,"实验异常停止：设置PCR扩增程序失败");
                processData.add(new ProcessBean(12, "实验异常停止：设置PCR扩增程序失败"));
                throw new RuntimeException("设置PCR扩增程序失败");
            }

            bundle = serialPortHelper.execute(Cmd.pcrOperation(deviceId, true));
            if (!bundle.getBoolean("suc")) {
                LoggerUtil.Log(deviceId,name,"实验异常停止：执行PCR扩增程序失败");
                processData.add(new ProcessBean(12, "实验异常停止：执行PCR扩增程序失败"));
                throw new RuntimeException("执行PCR扩增程序失败");
            }

            pcrBeginTime = System.currentTimeMillis();

            //直到检测到na正确结束  本阶段执行完毕
            while (pcrState < 2) {
                Thread.sleep(1000);
                //查询运行状态00  结果4个字节 第一字节01234 空闲运行自停手停异停  234字节代表段序号节点序号循环数
                //查询温度01      结果2个字节 高位在前 除以100得到浮点数结果
                //查询实时荧光02   结果9个字节  第一字节代表第几次荧光数据 剩余8个字节 每2个代表1个通道的数据
                bundle = serialPortHelper.execute(Cmd.query(deviceId, 0, 1, 2));
                if (!bundle.getBoolean("suc")){

                    timeOutCount++;
                    if (timeOutCount>=3){
                        processData.add(new ProcessBean(12, "实验异常停止：三次读取实验状态失败"));
                        throw new RuntimeException("三次读取实验状态失败");
                    }
                    continue;
                }
                timeOutCount=0;
                byte[] data = bundle.getByteArray("data");
                if (data == null) continue;

                //data7 是length(状态数)
                //data8 应该是00（第一个状态的查询码,00表示运行状态）
                pcrState = data[9] & 0xff;//data9 运行状态
                currentStageIndex = data[10] & 0xff;
                currentSectionIndex = data[11] & 0xff;
                runningCycleInStage = data[12] & 0xff;

                if (pcrState != 1)  Logger.e(deviceId+":PCR程序当前状态:" + pcrState);


                //data13 应该是01
                int tecTemp = ((data[14] & 0xff) << 8) + (data[15] & 0xff);
                Log.e(TAG, "温度" + tecTemp);
                //设置当前温度
                currentTemp = tecTemp / 100f;
                int sec = (int) (System.currentTimeMillis() - pcrBeginTime) / 1000;
                tempList.add(new TempBean(tecTemp, sec));

                //data16 应该是2
                int lIndex = data[17] & 0xff;
                if (lIndex > lightIndex) {
                    lightIndex = lIndex;
                    totalRunCycleCount = lightIndex;
                    float[] coeffs = ConfigManager.getInstance().getSettingInfo().getCoefficients();
                    for (int i = 0; i < 4; i++) {
                        int value = ((data[18 + i * 2] & 0xff) << 8) + (data[19 + i * 2] & 0xff);
                        lightData.get(1000 + i).add(new LightBean(lightIndex, value));
                        if (coeffs != null && coeffs[i] != 0) {
                            value *= coeffs[i];
                        }
                        lightData.get(i).add(new LightBean(lightIndex, value));
                    }
                    processData.add(new ProcessBean(3, "第" + lightIndex + "次采光完毕"));
                    LoggerUtil.Log(deviceId,name,"第" + lightIndex + "次采光完毕");
                    if (onIntensityListener != null) {
                        onIntensityListener.onDataReceive();
                    }
                }
                if (onSectionProcessListener != null && currentStageIndex > 0) {
                    int runningTotalSection = getRunningTotalSection();
                    onSectionProcessListener.onSectionProcess(runningTotalSection, runningCycleInStage, isCurrentStageCycle);
                }

            }

            processData.add(new ProcessBean(10, "PCR扩增结束"));
            LoggerUtil.Log(deviceId,name,"PCR扩增结束");
        }
        processData.add(new ProcessBean(12, "实验结束"));
        LoggerUtil.Log(deviceId,name,"实验结束");
        return true;
    }

    @Override
    public List<TempBean> getTempList() {
        return tempList;
    }

    @Override
    public SparseArray<List<LightBean>> getLightData() {
        return lightData;
    }

    @Override
    public List<ProcessBean> getProcessData() {
        return processData;
    }

    @Override
    public Bundle getRunSummary() {
        Bundle bundle = new Bundle();
        //开始时间
        bundle.putLong("beginTime", beginTime);
        //结束时间
//        bundle.putLong("endTime", endTime);
        //预计剩余时间
        leftTime = 0;
        long runTime = System.currentTimeMillis() - beginTime;
        if ((planTime - runTime) >= 0) {
            leftTime = (planTime - runTime) / 1000;
        } else {
            //当预估剩余时间小于0时,更新预计运行时间和剩余时间
//            updatePlanTime();
            leftTime = 0;
        }
        bundle.putLong("leftTime", leftTime);
        //预计运行时间
        bundle.putLong("planTime", planTime);
        //温度
        bundle.putFloat("tecTemp", currentTemp);
        //程序总循环数
        bundle.putInt("totalCycle", totalCycle);
        //当前段序号
        bundle.putInt("currentStageIndex", currentStageIndex);
        //当前节序号
        bundle.putInt("currentSectionIndex", currentSectionIndex);

        //累加运行到第几循环（包含多个循环段的情况下）
        bundle.putInt("totalRunCycleCount", totalRunCycleCount);
        int runningTotalSection = getRunningTotalSection();
        //当前段是否循环段
        bundle.putBoolean("isCurrentStageCycle", isCurrentStageCycle);
        //当前段中运行到第几循环
        bundle.putInt("runningCycleInStage", runningCycleInStage);
        //累计节数
        bundle.putInt("runningTotalSection", runningTotalSection);
        return bundle;
    }

    private int getRunningTotalSection() {
        int runningTotalSection = 0;
        for (int i = 0; i < pcrProgram.size(); i++) {
            if (i == currentStageIndex - 1) {
                runningTotalSection = runningTotalSection + currentSectionIndex;
                isCurrentStageCycle = pcrProgram.get(i).getCycleCount() > 1;
                break;
            }
            ProgramStage programStage = pcrProgram.get(i);
            runningTotalSection = runningTotalSection + programStage.getSections().size();

        }
        return runningTotalSection;
    }

    @Override
    public boolean isPcrProcessing() {
        return pcrState == 1;
    }

    @Override
    public void finalOperation(int finishCause) {
        try {
            serialPortHelper.execute(Cmd.pcrOperation(deviceId, false));
        } catch (InterruptedException e) {
        }
        if (finishCause == EXP_FINISH_MANUALLY) {
            LoggerUtil.Log(deviceId,name,"实验被手动终止");
            processData.add(new ProcessBean(12, "实验被手动终止"));
        } else {
            LoggerUtil.Log(deviceId,name,"finalOperation: 实验停止原因" + finishCause);
        }
    }

    public void setOnIntensityListener(OnIntensityListener onIntensityListener) {
        this.onIntensityListener = onIntensityListener;
    }

    public ExpInfo getExperimentInfo() {
        return expInfo;
    }

    public void setOnSectionProcessListener(OnSectionProcessListener listener) {
        this.onSectionProcessListener = listener;
    }
}